import { Toast } from 'antd-mobile'
import { useEffect, useMemo, useRef } from 'react'
import { createContainer } from 'unstated-next'
import type { SocketInitialInterface } from './interface'
import { history } from 'umi'

/** 重连间隔 */
let lockReconnect: any
let timeout: any
let reconnectCount: number = 5 // 重连次数

/** 心跳相关 */
let interval: any
let receiveTime: any

function useWebSocket(initialState?: SocketInitialInterface) {
    const { url, time = 3000, params = {}, init, noReconnect = false } = initialState || {}
    const ws = useRef<WebSocket | null>(null)
    const messageQueue = useRef<any[]>([]) /** 未连接是发送的消息队列 */
    const listeners = useRef<Record<string, ((data: any) => void)[]>>({}) /** 订阅数组 特殊字段'ws_connect_closed' 连接断开 */

    /** 发送 */
    const send = (action_type: string, message: Record<string, any>) => {
        const msg = { action_type, ...message }
        if (ws.current?.readyState === WebSocket.OPEN) {
            ws.current?.send(JSON.stringify({ ...msg, ...params }))
        } else {
            messageQueue.current.push(msg)
        }
    }

    /** 订阅 */
    const onMessage = (action_type: string, callback: (val: any) => void) => {
        console.log('onMessage', action_type, listeners.current)
        if (listeners?.current && !listeners?.current?.[action_type]) {
            listeners.current[action_type] = []
        }

        listeners?.current?.[action_type]?.push(callback)
    }

    /** 取消订阅 */
    const remove = (action_type: string, callback: (val: any) => void) => {
        if (listeners?.current[action_type]) {
            listeners.current[action_type] = listeners?.current?.[action_type]?.filter(cb => {
                return cb !== callback
            })
        }
    }

    const keepalive = useMemo(() => {
        function heartbeat() {
            /** 保持心跳 */
            return () => {
                if (interval) {
                    clearInterval(interval)
                }
                interval = setInterval(() => {
                    // console.log(new Date().valueOf(), receiveTime, time)
                    /** 当前时间 - 最近接收时间 大于 间隔时间 */
                    if (new Date().valueOf() - receiveTime > 0.9 * time) {
                        /** 超出一个间隔没有收到消息重连 */
                        if (new Date().valueOf() - receiveTime > 1.9 * time) {
                            reconnect()
                            return
                        }
                        send('keepalive', {})
                    }
                }, time)
            }
        }
        return heartbeat()
    }, [])

    /** 初始化连接 */
    const connect = (isReconnect?: boolean) => {
        ws.current = new WebSocket(url as string)

        /** 连接成功 */
        ws.current.addEventListener('open', () => {
            console.log('WebSocket connected')
            lockReconnect = false
            /** 开启心跳连接 */
            keepalive()
            /** 重连初始化操作 */
            if (isReconnect) {
                send('reconnect', {})
            }
            receiveTime = new Date().valueOf()
            messageQueue.current.forEach(message => {
                const { type } = message
                send(type, message)
            })
            messageQueue.current = []
        })

        /** 监听信息 */
        ws.current.addEventListener('message', event => {
            const response = JSON.parse(event.data)
            const { action_type } = response
            console.log('Received message:', action_type, response)
            reconnectCount = 5
            listeners?.current?.[action_type]?.forEach(cb => cb(response))
            receiveTime = new Date().valueOf()

            if (action_type === 'error') {
                const { msg, code } = response?.data || {}
                /** 特殊 code */
                switch (code) {
                    case 405:
                    case 406:
                        /** 只允许存在单个进程 多开时关闭并提示 */
                        ws.current?.close(1000)
                        Toast.show({
                            maskClickable: false,
                            content: <span>{msg}</span>,
                            // duration: 0,
                            position: 'center',
                        })
                        history.goBack()
                        break
                    case 401:
                        /** 登录失效 */
                        ws.current?.close(1000)
                        Toast.show({
                            maskClickable: false,
                            content: '你的登录已失效，请关闭当前页面重新进入',
                            // duration: 0,
                            position: 'center',
                        })
                        history.goBack()
                        break

                    default:
                        msg && Toast.show(msg)
                        break
                }
            }
        })

        /** 监听错误 */
        ws.current.addEventListener('error', error => {
            console.error('WebSocket error:', error)
            /** 重连 */
            reconnect()
        })

        /** 监听关闭 */
        ws.current.addEventListener('close', message => {
            console.log('WebSocket closed', message)
            const { code } = message
            /** 关闭心跳机制防止重连 */
            clearInterval(interval)
            /** 清除待发送消息 */
            messageQueue.current = []
            /** 正常关闭  1000 */
            if (code !== 1000) {
                /** 重连 */
                reconnect()
            }
        })
    }

    /** 重连 */
    function reconnect() {
        if (noReconnect) {
            return
        }
        console.log(timeout, reconnectCount)
        if (lockReconnect || reconnectCount <= 0) {
            return
        }
        lockReconnect = true
        //没连接上会一直重连，设置延迟避免请求过多
        ws.current?.close(1000)
        connect(true)
        reconnectCount = reconnectCount - 1
    }

    useEffect(() => {
        /** 初始连接 */
        reconnectCount = 5
        connect()
        init?.({
            send,
            onMessage,
            close: () => {
                ws?.current?.close(1000)
            },
        })

        return () => {
            if (ws.current) {
                ws.current.close(1000)
            }
            clearInterval(interval)
            clearTimeout(timeout)
        }
    }, [])

    return {
        send,
        onMessage,
        remove,
        connect: () => {
            ws.current?.close(1000)
            init?.({
                send,
                onMessage,
                close: () => {
                    ws?.current?.close(1000)
                },
            })
            connect()
        },
        webSocketIns: () => ws.current,
    }
}

export const SocketContainer = createContainer(useWebSocket)
